package com.lagou.edu.tpmonitor.manager;

import com.lagou.edu.tpmonitor.model.TimeMonitor;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author liangzj
 * @date 2021/5/20 22:03
 */
public class PTMonitorManager {

    private static final int MONITOR_RANGE = 1;

    /**
     * 记录不同方法执行时间的集合
     */
    // Map<methodName, List<methodRunningTime>>
    private static final Map<String, List<TimeMonitor>> METHOD_TIME_MONITOR_MAP = new ConcurrentHashMap<>();

    {
        monitorStart();
    }

    private void monitorStart() {
        new Thread(() -> {
            // 每5s输出一次
            int second = 0;
            for (; ; ) {
                try {
                    TimeUnit.SECONDS.sleep(5);

                    // 计算top90、top99
                    Map<String, Long> top90Map = mgetMethodTopIndicator(MONITOR_RANGE, 90);
                    Map<String, Long> top99Map = mgetMethodTopIndicator(MONITOR_RANGE, 99);

                    for (String method : METHOD_TIME_MONITOR_MAP.keySet()) {
                        System.out.println(String.format("%s: top90=%d, top99=%d, requestTime=%d",
                                method, top90Map.get(method), top99Map.get(method), METHOD_TIME_MONITOR_MAP.get(method).size()));
                    }
                    System.out.println("================================================ " + (second += 5) + "s");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }).start();
    }

    /**
     * 各个方法的top指标计算
     *
     * @param range     指标计算范围，单位分钟(值n分钟内的指标)
     * @param indicator 指标（例如top90:indicator=90）
     * @return
     */
    private Map<String, Long> mgetMethodTopIndicator(int range, int indicator) {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime start = now.plusMinutes(-range);

        Set<String> methods = METHOD_TIME_MONITOR_MAP.keySet();

        Map<String, Long> methodTopIndicatorMap = new HashMap<>();
        for (String method : methods) {
            long topIndicator = topIndicator(indicator, start, now, METHOD_TIME_MONITOR_MAP.get(method));
            methodTopIndicatorMap.put(method, topIndicator);
        }

        return methodTopIndicatorMap;
    }

    /**
     * top指标计算
     *
     * @param indicator
     * @param start
     * @param end
     * @param timeMonitors
     * @return
     */
    private long topIndicator(int indicator, LocalDateTime start, LocalDateTime end, List<TimeMonitor> timeMonitors) {

        long startMilli = start.toInstant(ZoneOffset.of("+8")).toEpochMilli();
        long endMilli = end.toInstant(ZoneOffset.of("+8")).toEpochMilli();

        // 获取计算范围内的执行时间
        List<TimeMonitor> ascRangeTimeMonitor = timeMonitors.stream()
                .filter(timeMonitor -> (timeMonitor.getStartTime() >= startMilli && timeMonitor.getStartTime() <= endMilli))
                .sorted()
                .collect(Collectors.toList());

        if (CollectionUtils.isEmpty(ascRangeTimeMonitor)) {
            return 0;
        }

        int index = countIndicatorIndex(ascRangeTimeMonitor.size(), indicator);

        System.out.println(String.format("top%d rangeCount=%d", indicator, ascRangeTimeMonitor.size()));

        return ascRangeTimeMonitor.get(index).getRunningTime();
    }

    /**
     * 计算取值的下标
     *
     * @param total
     * @param indicator
     * @return
     */
    private int countIndicatorIndex(int total, int indicator) {
        BigDecimal totalDecimal = BigDecimal.valueOf(total);
        BigDecimal indicatorDecimal = BigDecimal.valueOf(indicator);
        BigDecimal percentage = indicatorDecimal.divide(BigDecimal.valueOf(100));

        BigDecimal indexDecimal = totalDecimal.multiply(percentage);

        int index = indexDecimal.intValue();

        return index > total ? total : index;
    }

    /**
     * 记录方法执行时长
     *
     * @param methodName  方法名
     * @param timeMonitor
     */
    public synchronized void recordMonitor(String methodName, TimeMonitor timeMonitor) {

        if (METHOD_TIME_MONITOR_MAP.get(methodName) == null) {
            METHOD_TIME_MONITOR_MAP.put(methodName, new CopyOnWriteArrayList<>());
        }

        METHOD_TIME_MONITOR_MAP.get(methodName).add(timeMonitor);

    }

}
